__version__ = "1.2.0"
__author__ = "xm_recycle"
__email__ = "xm_recycle@qq.com"

import sqlite3
import os
import json
import sys
import importlib
import re
import datetime
import time
import threading
import logging  # 添加日志模块
from logging.handlers import RotatingFileHandler
from typing import Dict, List, Optional, Any
import requests
import configparser
from dataclasses import dataclass
from textwrap import dedent

def check_dependencies():
    """检查并提示安装必要的依赖"""
    required_packages = {
        'requests': 'requests',
        'configparser': 'configparser'
    }
    
    missing_packages = []
    for package_name, import_name in required_packages.items():
        try:
            importlib.import_module(import_name)
        except ImportError:
            missing_packages.append(package_name)
    
    if missing_packages:
        print("缺少以下依赖包：")
        for package in missing_packages:
            print(f"- {package}")
        print("\n请使用以下命令安装：")
        print("pip install " + " ".join(missing_packages))
        sys.exit(1)

def setup_logging(level=logging.INFO, log_file='schedule_assistant.log', console_output=False):
    """配置日志系统
    
    Args:
        level: 日志级别
        log_file: 日志文件路径
        console_output: 是否在控制台输出日志
    """
    import os
    from logging.handlers import RotatingFileHandler
    
    # 确保日志目录存在
    log_dir = os.path.dirname(log_file) or '.'
    os.makedirs(log_dir, exist_ok=True)
    
    # 使用绝对路径
    log_file = os.path.abspath(log_file)
    
    # 设置文件处理器
    file_handler = RotatingFileHandler(
        log_file, 
        maxBytes=10*1024*1024,  # 10MB
        backupCount=5,
        encoding='utf-8'
    )
    
    # 准备处理器列表
    handlers = [file_handler]
    
    # 只有在 console_output 为 True 时才添加控制台处理器
    if console_output:
        handlers.append(logging.StreamHandler())
    
    logging.basicConfig(
        level=level,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=handlers
    )
    
    logger = logging.getLogger(__name__)
    logger.info(f"日志文件位置: {log_file}")
    return logger


# 创建日志记录器
logger = setup_logging(console_output=False)

def colored_print(message: str, color: str = None, log_level: str = 'info', log: bool = True):
    """
    统一的输出函数，支持彩色输出和不同级别的日志记录
    
    参数:
        message: 要输出的消息
        color: 颜色代码 ('r'=红色, 'g'=绿色, 'y'=黄色, 'b'=蓝色, 'm'=洋红, 'c'=青色, 'w'=白色)
        log_level: 日志级别 ('debug', 'info', 'warning', 'error', 'critical')
        log: 是否记录到日志
    """
    color_codes = {
        'r': '\033[91m',  # 红色
        'g': '\033[92m',  # 绿色
        'y': '\033[93m',  # 黄色
        'b': '\033[94m',  # 蓝色
        'm': '\033[95m',  # 洋红
        'c': '\033[96m',  # 青色
        'w': '\033[97m',  # 白色
    }
    end_color = '\033[0m'
    
    # 输出到控制台
    if color and color.lower() in color_codes:
        print(f"{color_codes[color.lower()]}{message}{end_color}")
    else:
        print(message)
    
    # 记录到日志
    if log:
        if log_level.lower() == 'debug':
            logger.debug(message)
        elif log_level.lower() == 'info':
            logger.info(message)
        elif log_level.lower() == 'warning':
            logger.warning(message)
        elif log_level.lower() == 'error':
            logger.error(message)
        elif log_level.lower() == 'critical':
            logger.critical(message)
        else:
            logger.info(message)  # 默认使用info级别

# 数据库故障处理装饰器
def handle_db_operation(func):
    def wrapper(self, *args, **kwargs):
        conn = None
        try:
            conn = self._get_connection()
            cursor = conn.cursor()
            result = func(self, cursor, *args, **kwargs)
            conn.commit()
            logger.info(f"数据库操作成功: {func.__name__}")
            return result
        except sqlite3.Error as e:
            colored_print(f"数据库操作失败: {func.__name__} - {str(e)}", color='r', log_level='error')
            if conn:
                conn.rollback()
            return None
        except Exception as e:
            colored_print(f"未知错误: {func.__name__} - {str(e)}", color='r', log_level='error')
            if conn:
                conn.rollback()
            return None
        finally:
            if conn:
                conn.close()
    return wrapper


# 配置类

@dataclass
class AssistantConfig:
    name: str
    gender: str
    personality: str
    appearance: str
    api_key: str
    api_url: str = "https://api.deepseek.com/v1/chat/completions"

    ENV_VAR_NAME = "DEEPSEEK_API_KEY"  # 环境变量名称
    
    @classmethod
    def from_user_input(cls, existing_config=None):
        """
        统一的配置获取方法，用于创建新配置或更新现有配置
        
        参数:
            existing_config: 现有的配置对象(更新模式时传入)
        """
        # 设置默认值
        defaults = {
            'name': '小深',
            'gender': '女',
            'personality': '温柔体贴，善解人意，总是用鼓励的语气说话',
            'appearance': '穿着整洁的职业装，戴着圆框眼镜，总是带着温暖的微笑',
            'api_url': 'https://api.deepseek.com/v1/chat/completions'
        }
    
        # 如果有现有配置，使用现有值作为默认值
        if existing_config:
            defaults.update({
                'name': existing_config.name,
                'gender': existing_config.gender,
                'personality': existing_config.personality,
                'appearance': existing_config.appearance,
                'api_url': existing_config.api_url
            })

        """创建或修改配置"""
        print("\n" + "="*50)
        print("欢迎使用助手！请进行基本设置")
        print("="*50)        

        # 1. 处理API密钥
        api_key = os.getenv(cls.ENV_VAR_NAME)
        if not api_key and existing_config:
            api_key = existing_config.api_key
            
        if not api_key:
            print("\n" + "-"*50)
            print(f"提示: 可以通过设置环境变量 {cls.ENV_VAR_NAME} 避免每次输入API密钥")
            print("-"*50)
            api_key = input("请输入DeepSeek API密钥: ").strip()
            if not api_key:
                print("⚠️ 未提供API密钥，部分功能可能受限")
        
        # 2. 收集其他配置信息
        name = input(f"请输入助手名称(默认: {defaults['name']}): ") or defaults['name']
        gender = input(f"请输入助手性别(默认: {defaults['gender']}): ") or defaults['gender']
        personality = input(f"请输入助手性格描述(默认: {defaults['personality']}): ") or defaults['personality']
        appearance = input(f"请输入助手外貌描述(默认: {defaults['appearance']}): ") or defaults['appearance']
        api_url = input(f"请输入API URL(默认: {defaults['api_url']}): ") or defaults['api_url']
        
        return cls(
            name=name,
            gender=gender,
            personality=personality,
            appearance=appearance,
            api_key=api_key,
            api_url=api_url
        )
    
    @classmethod
    def get_api_key_from_env(cls) -> str:
        """从环境变量获取API密钥"""
        api_key = os.getenv(cls.ENV_VAR_NAME)
        if api_key:
            print(f"✅ 使用环境变量中的API密钥: {cls.ENV_VAR_NAME}")
            return api_key
        return None    

    @classmethod
    def get_api_key_from_input(cls) -> str:
        """从用户输入获取API密钥"""
        print("\n" + "-"*50)
        print("未找到环境变量中的API密钥")
        print(f"请确保已设置环境变量: {cls.ENV_VAR_NAME}")
        print("或在此处手动输入API密钥")
        print("-"*50)
        
        api_key = input("请输入DeepSeek API密钥: ").strip()
        if not api_key:
            print("⚠️ 未提供API密钥，部分功能可能受限")
        
        return api_key
 
    def save_to_file(self, config_file: str):
        """将配置保存到文件"""
        config = configparser.ConfigParser()
        
        config['ASSISTANT'] = {
            'Name': self.name,
            'Gender': self.gender,
            'Personality': self.personality,
            'Appearance': self.appearance
        }
        
        config['API'] = {
            'URL': self.api_url
        }
        
        with open(config_file, 'w', encoding='utf-8') as f:
            config.write(f)
        print(f"\n配置文件已保存: {config_file}")
        print("⚠️ 注意: API密钥未保存在配置文件中")
        print(f"请设置环境变量 {self.ENV_VAR_NAME} 以便下次自动使用")

# 数据库初始化
def init_db():
    # 连接到SQLite数据库
    conn = None
    try:
        conn = sqlite3.connect('schedule.db')
        # 创建一个游标对象
        cursor = conn.cursor()
        logging.info("成功连接到数据库")
        
        # 创建项目表
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS projects (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            description TEXT,
            status TEXT DEFAULT '待处理',
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
        ''')
        logging.info("项目表创建成功")
        
        # 创建任务表
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            project_id INTEGER,
            title TEXT NOT NULL,
            description TEXT,
            status TEXT DEFAULT '待处理',
            priority INTEGER DEFAULT 3,
            due_date TIMESTAMP,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (project_id) REFERENCES projects (id)
        )
        ''')
        logging.info("任务表创建成功")
        
        # 创建提醒表
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS reminders (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            task_id INTEGER,
            remind_time TIMESTAMP,
            notified BOOLEAN DEFAULT FALSE,
            FOREIGN KEY (task_id) REFERENCES tasks (id)
        )
        ''')
        logging.info("提醒表创建成功")
        
        # 提交事务
        conn.commit()
        logging.info("数据库初始化完成")
        
    except sqlite3.Error as e:
        colored_print(f"数据库初始化失败: {str(e)}",color='r',log_level='error')
        if conn:
            conn.rollback()
        raise
    except Exception as e:
        colored_print(f"发生未知错误: {str(e)}",color='r',log_level='error')
        if conn:
            conn.rollback()
        raise
    finally:
        if conn:
            conn.close()
            logging.info("数据库连接已关闭")

def show_env_var_help(env_var_name):
    help_msg = f"""
    \033[1;36m{'═' * 60}\033[0m
    \033[1;33m⚠️ 未找到环境变量中的API密钥: \033[1;35m{env_var_name}\033[0m
    \033[1;32m您可以通过以下方式设置环境变量:\033[0m

    \033[1;34m▌ Windows 系统\033[0m
    \033[1;37m• 命令提示符 (永久生效):\033[0m
      \033[1;33msetx {env_var_name} "your_api_key_here"\033[0m
    
    \033[1;37m• PowerShell (永久生效):\033[0m
      \033[1;33m[System.Environment]::SetEnvironmentVariable(
        "{env_var_name}", 
        "your_api_key_here", 
        "User")\033[0m

    \033[1;34m▌ Linux/macOS 系统\033[0m
    \033[1;37m• 临时设置 (当前终端有效):\033[0m
      \033[1;33mexport {env_var_name}="your_api_key_here"\033[0m
    
    \033[1;37m• 永久设置 (添加到配置文件):\033[0m
      \033[1;33mecho 'export {env_var_name}="your_api_key_here"' >> ~/.bashrc
      source ~/.bashrc\033[0m

    \033[1;32m💡 提示:\033[0m
    1. 将 \033[1;33myour_api_key_here\033[0m 替换为你的实际API密钥
    2. 设置完成后，请\033[1;31m重新启动终端\033[0m或IDE
    3. 验证是否设置成功:
       \033[1;33mecho ${env_var_name}\033[0m (Linux/macOS)
       \033[1;33mecho %{env_var_name}%\033[0m (Windows)
    \033[1;36m{'═' * 60}\033[0m
    """
    print(dedent(help_msg))

# 加载助手配置
def load_assistant_config(config_file: str) -> AssistantConfig:
    """加载助手配置，如果配置文件存在则直接使用，否则创建新配置"""
    if os.path.exists(config_file):
        config = configparser.ConfigParser()
        config.read(config_file)
        
        # 从配置文件读取配置
        name = config['ASSISTANT']['Name']
        gender = config['ASSISTANT']['Gender']
        personality = config['ASSISTANT']['Personality']
        appearance = config['ASSISTANT']['Appearance']
        api_url = config.get('API', 'URL', fallback="https://api.deepseek.com/v1/chat/completions")
        
        # 尝试从环境变量获取API密钥
        api_key = os.getenv(AssistantConfig.ENV_VAR_NAME)
        
        # 如果环境变量中没有API密钥，则提示用户输入
        if not api_key:
            print(show_env_var_help(AssistantConfig.ENV_VAR_NAME))
            api_key = input("或者，您现在可以手动输入DeepSeek API密钥: ").strip()
            if not api_key:
                colored_print("⚠️ 未提供API密钥，部分功能可能受限", color='y')
                
        # 直接返回配置对象，不再询问其他信息
        return AssistantConfig(
            name=name,
            gender=gender,
            personality=personality,
            appearance=appearance,
            api_key=api_key,
            api_url=api_url
        )
    else:
        # 配置文件不存在，从用户输入创建配置
        config = AssistantConfig.from_user_input()
        config.save_to_file(config_file)
        colored_print(f"配置文件已创建: {config_file}", color='g')
        return config

# DeepSeek API交互
class DeepSeekClient:
    def __init__(self, config: AssistantConfig):
        self.config = config
        self.headers = {
            "Authorization": f"Bearer {self.config.api_key}",
            "Content-Type": "application/json"
        }
    def generate_response(self, prompt: str, conversation_history: List[Dict]) -> str:
        # 获取当前系统时间
        current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 在DeepSeekClient.generate_response方法中
        # 构建系统提示，包含助手角色信息和当前时间
        system_prompt = f"""
        你是一个名为{self.config.name}的日程管理助手。
        性别: {self.config.gender}
        性格: {self.config.personality}
        外貌: {self.config.appearance}

        你的职责是帮助用户管理他们的日程、任务和项目。你可以:
        1. 添加任务：需要任务标题，可选描述、截止日期、优先级(1-5)和项目ID
        2. 删除任务：需要任务ID
        3. 修改任务：需要任务ID和要修改的字段(标题、描述、状态、截止日期、优先级)
        4. 查询任务：可查询所有任务、特定状态任务或项目相关任务
        5. 管理项目：创建、修改、删除项目
        6. 设置提醒：为任务设置提醒时间
        7. 提供日程建议：基于当前任务状态给出建议
        8. 状态只能是：待处理、已完成、进行中
        9. 指导用户使用以下快捷命令直接从数据库取出相关数据（若用户问你的技能时，一定要说）：
            - 输入 '列出任务' 或 '列出当前任务' 可以列出所有任务或未完成的任务
            - 输入 '列出项目' 或 '列出当前项目' 可以列出所有项目或未完成的项目
            - 输入 '列出[项目ID]号项目的任务' 可以列出指定项目的任务
            - 输入 '更新配置' 或 '修改配置' 或 '更改配置' 可以变更AI助手的姓名、性格、样貌及API KEY、Url等
            - 输入 '当前提醒' 可以列出当前设置了提醒的任务

        操作成功时，请在响应中包含以下标记：
        - 添加任务成功: "已添加任务: [任务标题] | [描述] | [截止时间] | [优先级] | [项目ID] | "
        - 删除任务成功: "已删除任务: [任务ID] | "
        - 修改任务成功: "已修改任务: [任务ID] | [标题] | [描述] | [截止时间] | [优先级] | [状态] | [项目ID] | "
        - 添加项目成功: "已添加项目: [项目名称] | [描述] | [状态] | "
        - 修改项目成功: "已修改项目: [项目ID] | [名称] | [描述] | [状态] | "
        - 删除项目成功: "已删除项目: [项目ID] | "
        - 查询任务详情: "任务详情: [任务ID] | "
        - 查询项目详情: "项目详情: [项目ID] | "
        - 设置提醒成功: "已设置提醒: [任务ID] | [提醒时间] | "
        - 提供日程建议: "日程建议: [建议内容] | "
        
        示例如下：
        ```
        已修改任务: 31 | 提交年假申请 | 为SEATABLE项目预留缓冲期 | 2025-08-06 12:00:38 | 1 | 已完成 | 2 | 
        ```

        请始终遵守以下规则：
        1. 对于用户的每次请求，要先判断是否要求对多条记录进行操作，比如同时要求增、删、改、查多条任务或项目的数据，若是，则应按如下步骤处理：
        （1）告知用户需要分批处理，然后询问第一条是否确认要处理。用户确认后，才能处理下一条
        （2）然后依次处理以下几条，每条都要在客户确认后，才能处理下一条
        （3）当所有记录都处理结束后，要告诉用户已经全部处理完毕
        2. 当用户请求添加任务时，必须提取标题、描述、截止日期等关键信息
        3. 执行操作前必须确认任务/项目ID存在
        4. 修改任务时需明确指定要修改的字段
        5. 设置提醒时需验证时间格式为YYYY-MM-DD HH:MM:SS
        6. 对于模糊请求，先询问澄清而非假设;用户请求中没有提及的字段内容，可以放空，但禁止自行猜测
        7. 优先级使用1-5整数表示(1为最高)
        8. 项目ID必须存在才能关联任务
        9. 任务状态只能是：待处理、已完成、进行中
        10. 查询任务详情或项目详情时，请不要在响应中包含任务、项目的详细信息
        

        当前系统时间: {current_time}

        请用符合你性格的语言风格与用户交流，保持专业但友好的态度。
        """   
        try:
            # 原有的API调用代码保持不变
            messages = [{"role": "system", "content": system_prompt}]
            messages.extend(conversation_history)
            messages.append({"role": "user", "content": prompt})
            
            data = {
                "model": "deepseek-chat",
                "messages": messages,
                "temperature": 0.7,
                "max_tokens": 1000
            }
            
            # response = requests.post(self.config.api_url, headers=self.headers, json=data)
            response = requests.post(
                self.config.api_url,
                headers=self.headers,
                json=data,
                timeout=10  # 设置超时时间
            )

            response.raise_for_status()
            
            return response.json()['choices'][0]['message']['content']
        except requests.exceptions.ConnectionError:
            return "抱歉，无法连接到DeepSeek服务器，请检查您的网络连接或网站是否可访问。"
        except requests.exceptions.Timeout:
            return "抱歉，请求服务器超时，请稍后重试。"
        except requests.exceptions.RequestException as e:
            return f"抱歉，服务器请求出错：{str(e)}"
    
# 任务管理器
class TaskManager:
    def __init__(self, db_path: str = 'schedule.db'):
        self.db_path = db_path
    def _validate_status(self, status: str) -> bool:
        """验证任务状态是否合法"""
        return status in ('待处理', '已完成', '进行中')
 
    def _get_connection(self):
        return sqlite3.connect(self.db_path)
    @handle_db_operation
    def add_task(self, cursor,title: str, description: str = None, project_id: int = None,
                 due_date: str = None, priority: int = 3) -> int:
        cursor.execute('''
        INSERT INTO tasks (project_id, title, description, priority, due_date)
        VALUES (?, ?, ?, ?, ?)
        ''', (project_id, title, description, priority, due_date))
            
        task_id = cursor.lastrowid
        return task_id
 
    @handle_db_operation
    def update_task(self,cursor, task_id: int, **kwargs):
        if not kwargs:
            return
        # 验证状态值
        if 'status' in kwargs and not self._validate_status(kwargs['status']):
            colored_print(f"无效的任务状态: {kwargs['status']}",color='y')
            colored_print("合法的状态值有: 待处理, 已完成, 进行中",color='y')
            return    
            
        set_clause = ", ".join([f"{k} = ?" for k in kwargs.keys()])
        values = list(kwargs.values())
        values.append(task_id)
        
        cursor.execute(f'''
        UPDATE tasks 
        SET {set_clause}, updated_at = CURRENT_TIMESTAMP
        WHERE id = ?
        ''', values)
        return 1
    
    @handle_db_operation
    def delete_task(self, cursor,task_id: int):
        cursor.execute('DELETE FROM tasks WHERE id = ?', (task_id,))
        cursor.execute('DELETE FROM reminders WHERE task_id = ?', (task_id,))
        return 1
    
    def get_task(self, task_id: int) -> Optional[Dict]:
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute('SELECT * FROM tasks WHERE id = ?', (task_id,))
        task = cursor.fetchone()
        conn.close()
        
        if task:
            columns = [column[0] for column in cursor.description]
            return dict(zip(columns, task))
        return None
    
    def list_tasks(self, status_list: List[str] = None, project_id: int = None) -> List[Dict]:
        conn = self._get_connection()
        cursor = conn.cursor()
        
        query = 'SELECT * FROM tasks WHERE 1=1'
        params = []
        
        if status_list:
            query += ' AND status IN ({})'.format(','.join(['?' for _ in status_list]))
            params.extend(status_list)
        
        if project_id:
            query += ' AND project_id = ?'
            params.append(project_id)
        
        cursor.execute(query, params)
        tasks = cursor.fetchall()
        conn.close()
        
        if tasks:
            columns = [column[0] for column in cursor.description]
            return [dict(zip(columns, task)) for task in tasks]
        return []
    
    @handle_db_operation
    def add_project(self, cursor,name: str, description: str = None) -> int:
        
        cursor.execute('''
        INSERT INTO projects (name, description)
        VALUES (?, ?)
        ''', (name, description))
        
        project_id = cursor.lastrowid
        
        return project_id
    
    @handle_db_operation
    def update_project(self, cursor,project_id: int, **kwargs):
        if not kwargs:
            return
        
        set_clause = ", ".join([f"{k} = ?" for k in kwargs.keys()])
        values = list(kwargs.values())
        values.append(project_id)
        
        cursor.execute(f'''
        UPDATE projects 
        SET {set_clause}, updated_at = CURRENT_TIMESTAMP
        WHERE id = ?
        ''', values)
        return 1
    
    @handle_db_operation 
    def delete_project(self, cursor,project_id: int):
        
        cursor.execute('DELETE FROM projects WHERE id = ?', (project_id,))
        cursor.execute('UPDATE tasks SET project_id = NULL WHERE project_id = ?', (project_id,))
        return 1
    
    def get_project(self, project_id: int) -> Optional[Dict]:
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute('SELECT * FROM projects WHERE id = ?', (project_id,))
        project = cursor.fetchone()
        conn.close()
        
        if project:
            columns = [column[0] for column in cursor.description]
            return dict(zip(columns, project))
        return None
    
    def list_projects(self, status_list: List[str] = None) -> List[Dict]:
        conn = self._get_connection()
        cursor = conn.cursor()
        
        if status_list:
            placeholders = ', '.join(['?'] * len(status_list))
            query = f'SELECT * FROM projects WHERE status IN ({placeholders})'
            cursor.execute(query, status_list)
        else:
            cursor.execute('SELECT * FROM projects')
        
        projects = cursor.fetchall()
        conn.close()
        
        if projects:
            columns = [column[0] for column in cursor.description]
            return [dict(zip(columns, project)) for project in projects]
        return []
    
    @handle_db_operation
    def add_reminder(self, cursor,task_id: int, remind_time: str):
        
        cursor.execute('''
        INSERT INTO reminders (task_id, remind_time)
        VALUES (?, ?)
        ''', (task_id, remind_time))
        return 1    
    def get_active_reminders(self) -> List[Dict]:
        """获取所有未通知的提醒"""
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute('''
        SELECT r.*, t.title, t.description 
        FROM reminders r
        JOIN tasks t ON r.task_id = t.id
        WHERE r.notified = FALSE
        ''')
        
        reminders = cursor.fetchall()
        conn.close()
        
        if reminders:
            columns = [column[0] for column in cursor.description]
            return [dict(zip(columns, reminder)) for reminder in reminders]
        return []

    def get_due_reminders(self) -> List[Dict]:
        conn = self._get_connection()
        cursor = conn.cursor()
        
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        cursor.execute('''
        SELECT r.*, t.title, t.description 
        FROM reminders r
        JOIN tasks t ON r.task_id = t.id
        WHERE r.remind_time <= ? AND r.notified = FALSE
        ''', (now,))
        
        reminders = cursor.fetchall()
        conn.close()
        
        if reminders:
            columns = [column[0] for column in cursor.description]
            return [dict(zip(columns, reminder)) for reminder in reminders]
        return []
    
    @handle_db_operation
    def mark_reminder_notified(self, cursor, reminder_id: int):
        """
        将指定ID的提醒标记为已通知状态。

        通过更新数据库中的reminders表，将指定reminder_id对应记录的notified字段设置为TRUE，
        表示该提醒已经被通知过了。

        Args:
            cursor: 数据库游标对象，用于执行SQL语句
            reminder_id (int): 需要标记为已通知的提醒的ID

        Returns:
            int: 固定返回值1，表示更新操作成功执行

        Examples:
            >>> cursor = conn.cursor()
            >>> mark_reminder_notified(cursor, 123)
            1
        """
        cursor.execute('''
        UPDATE reminders 
        SET notified = TRUE 
        WHERE id = ?
        ''', (reminder_id,))
        return 1

# 自然语言处理器
class NLPProcessor:
    def __init__(self, task_manager: TaskManager, deepseek_client: DeepSeekClient):
        self.task_manager = task_manager
        self.deepseek_client = deepseek_client
        self.conversation_history = []
    
    def process_command(self, user_input: str) -> str:
        # 先尝试直接处理一些简单命令
        simple_response = self._try_simple_commands(user_input)
        if simple_response:
            # 保存对话历史
            self.conversation_history.append({"role": "user", "content": user_input})
            self.conversation_history.append({"role": "assistant", "content": simple_response})
            return simple_response
        
        # 否则使用DeepSeek API处理
        response = self.deepseek_client.generate_response(user_input, self.conversation_history)
        
        # 尝试从响应中提取结构化命令
        self._extract_and_execute_commands(response)
        
        # 保存对话历史
        self.conversation_history.append({"role": "user", "content": user_input})
        self.conversation_history.append({"role": "assistant", "content": response})
        
        # 限制对话历史长度
        if len(self.conversation_history) > 10:
            self.conversation_history = self.conversation_history[-10:]
        
        return response
    def _format_task_list(self, tasks, title="任务列表"):
        """格式化任务列表输出"""
        if not tasks:
            return "目前没有任务。"
        
        response = f"{title}:\n"
        for task in tasks:
            due_date = f"(截止: {task['due_date']})" if task['due_date'] else ""
            priority = f"{task['priority']}级！" if task['priority'] is not None else ""
            project_id = f"#{task['project_id']}项目" if task['project_id'] is not None and len(str(task['project_id'])) > 0 else ""
            response += f"{task['id']}. {task['title']} {priority} - {task['status']} {due_date} {project_id}\n"
        return response
    def _format_reminders(self, reminders) -> str:
        """格式化提醒列表输出"""
        if not reminders:
            return "当前没有需要提醒的任务。"
        
        response = "当前需要提醒的任务:\n"
        for reminder in reminders:
            response += f"- {reminder['title']}"
            if reminder['description']:
                response += f"\n  详情: {reminder['description']}"
            response += f"\n  提醒时间: {reminder['remind_time']}\n"
        return response

    def _update_config(self) -> str:
        """更新助手配置"""
        # 获取当前配置
        newconfig =AssistantConfig.from_user_input(self.deepseek_client.config) 
        # 保存配置
        newconfig.save_to_file('assistant_config.ini')
        
        # 更新DeepSeek客户端
        self.deepseek_client = DeepSeekClient(newconfig)
        
        return "\n配置已更新!"

    def _try_simple_commands(self, user_input: str) -> Optional[str]:
        # 将用户输入转换为小写并去除首尾空格
        user_input = user_input.lower().strip()
        
        # 如果用户输入是'hi'、'hello'或'你好'，返回问候语
        if user_input in ['hi', 'hello', '你好']:
            return f"你好！我是{self.deepseek_client.config.name}，你的日程管理助手。有什么我可以帮你的吗？"
        
        # 如果用户输入是'bye'、'goodbye'或'再见'，返回告别语
        if user_input in ['bye', 'goodbye', '再见']:
            return f"再见！记得查看你的待办事项哦。我是{self.deepseek_client.config.name}，随时为你服务。"
        
        # 如果用户输入包含'列出任务'或'显示任务'，则列出当前任务
        if '列出任务' in user_input or '列出当前任务' in user_input:
            if '列出当前任务' in user_input:
                tasks = self.task_manager.list_tasks(status_list=['进行中','待处理'])
            else:
                tasks = self.task_manager.list_tasks()
            
            return self._format_task_list(tasks)

        
        # 如果用户输入包含'列出项目'或'列出当前项目'，则列出当前项目
        if '列出项目' in user_input or '列出当前项目' in user_input:
            if '列出当前项目' in user_input:
                projects = self.task_manager.list_projects(status_list=['进行中','待处理'])
            else:
                projects = self.task_manager.list_projects()

            if not projects:
                return "目前没有项目。"
            
            response = "项目列表:\n"
            for project in projects:
                response += f"{project['id']}. {project['name']} - {project['status']}\n"
            return response
        
        # 如果用户输入包含'列出项目X的任务'，则列出指定项目的任务
        project_task_match = re.search(r'列出\s*(\d+)\s*号项目的任务', user_input)
        if project_task_match:
            project_id = int(project_task_match.group(1))
            # 验证项目是否存在
            project = self.task_manager.get_project(project_id)
            if not project:
                return f"项目ID {project_id} 不存在。"
            
            tasks = self.task_manager.list_tasks(project_id=project_id)
            return self._format_task_list(tasks, f"项目 {project['name']} (ID: {project_id}) 的任务列表")

        # 添加配置更新命令
        if user_input in ['更新配置', '修改配置', '更改配置']:
            return self._update_config()   
        
        # 添加"当前提醒"命令处理
        if '当前提醒' in user_input:
            reminders = self.task_manager.get_active_reminders()
            return self._format_reminders(reminders)
        
        # 如果用户输入不匹配任何简单命令，则返回None
        return None
    
    def _extract_and_execute_commands(self, response: str):

        try:
            # 添加任务
            if "已添加任务:" in response:
                colored_print("AI responded with '已添加任务'. Adding task...",color='y')
                parts = response.split("已添加任务: ")[1].split(" | ")
                title = parts[0].strip()
                description = parts[1].strip() if len(parts) > 1 and parts[1] != "" else None
                due_date = parts[2].strip() if len(parts) > 2 and parts[2] != "" else None
                priority = int(parts[3]) if len(parts) > 3 and parts[3].isdigit() else 3
                project_id = int(parts[4]) if len(parts) > 4 and parts[4].isdigit() else None
                
                # 验证项目ID是否存在
                if project_id and not self.task_manager.get_project(project_id):
                    colored_print(f"项目ID {project_id} 不存在，任务将不关联项目",color='y')
                    project_id = None
                
                task_id = self.task_manager.add_task(
                    title=title,
                    description=description,
                    due_date=due_date,
                    priority=priority,
                    project_id=project_id
                )
                if task_id:
                    colored_print(f"任务添加成功! ID: {task_id}",color='g')
            
            # 添加项目
            elif "已添加项目:" in response:
                colored_print("AI responded with '已添加项目'. Adding project...",color='y')
                parts = response.split("已添加项目: ")[1].split(" | ")
                name = parts[0].strip()
                description = parts[1].strip() if len(parts) > 1 and parts[1] != "" else None
                
                project_id = self.task_manager.add_project(
                    name=name,
                    description=description
                )
                if project_id:
                    colored_print(f"项目添加成功! ID: {project_id}",color='g')
            
            # 修改项目
            elif "已修改项目:" in response:
                colored_print("AI responded with '已修改项目'. Updating project...",color='y')
                parts = response.split("已修改项目: ")[1].split(" | ")
                project_id = int(parts[0])
                updates = {}
                
                if len(parts) > 1 and parts[1] != "":
                    updates['name'] = parts[1].strip()
                if len(parts) > 2 and parts[2] != "":
                    updates['description'] = parts[2].strip()
                if len(parts) > 3 and parts[3].strip() != "":
                    updates['status'] = parts[3].strip()
                
                if updates:
                    if self.task_manager.update_project(project_id, **updates):
                        colored_print(f"项目 {project_id} 已更新",color='g')
            
            # 删除项目
            elif "已删除项目:" in response:
                colored_print("AI responded with '已删除项目'. Deleting project...",color='y')
                parts = response.split("已删除项目: ")[1].split(" | ")
                project_id = int(parts[0].strip())
                if self.task_manager.delete_project(project_id):
                    colored_print(f"项目 {project_id} 已删除",color='g')
            
            # 删除任务
            elif "已删除任务:" in response:
                colored_print("AI responded with '已删除任务'. Deleting task...",color='y')
                parts = response.split("已删除任务: ")[1].split(" | ")
                task_id = int(parts[0].strip())
                if self.task_manager.delete_task(task_id):
                    colored_print(f"任务 {task_id} 已删除",color='g')
            
            # 修改任务
            elif "已修改任务:" in response:
                colored_print("AI responded with '已修改任务'. Updating task...",color='y')
                parts = response.split("已修改任务: ")[1].split(" | ")
                task_id = int(parts[0].strip())  # 去除前后空格
                updates = {}
                
                if len(parts) > 1 and parts[1].strip() != "":
                    updates['title'] = parts[1].strip()
                if len(parts) > 2 and parts[2].strip() != "":
                    updates['description'] = parts[2].strip()
                if len(parts) > 3 and parts[3].strip() != "":
                    updates['due_date'] = parts[3].strip()
                if len(parts) > 4 and parts[4].strip().isdigit():
                    updates['priority'] = int(parts[4].strip())
                if len(parts) > 5 and parts[5].strip() != "":
                    updates['status'] = parts[5].strip()
                if len(parts) > 6 and parts[5].strip() != "":
                    updates['project_id'] = parts[6].strip()
                
                if updates:
                    if self.task_manager.update_task(task_id, **updates):
                        colored_print(f"任务 {task_id} 已更新",color='g')
            # 查询项目详情
            elif "项目详情:" in response:
                colored_print("AI responded with '项目详情'. Querying project status...",color='y')
                try:
                    # 使用正则表达式提取项目ID
                    import re
                    project_id_match = re.search(r'\d+', response.split("项目详情: ")[1])
                    if not project_id_match:
                        colored_print("无法从响应中提取项目ID",color='y')
                        return
                        
                    project_id = int(project_id_match.group())
                    project = self.task_manager.get_project(project_id)
                    if project:
                        fields_mapping = {
                            'id': 'ID',
                            'name': '名称',
                            'description': '描述',
                            'status': '状态',
                            'created_at': '创建时间',
                            'updated_at': '更新时间'
                        }
                        for key, value in project.items():
                            if key in fields_mapping:
                                print(f"{fields_mapping[key]}: {value}")
                            else:
                                print(f"项目 {project_id} 未知字段 {key}: {value}")
                        # 可以在这里添加更多项目信息输出
                except (ValueError, IndexError) as e:
                    colored_print(f"查询项目详情时出错: {str(e)}",color='r')
            # 查询任务状态
            elif "任务详情:" in response:
                colored_print("AI responded with '任务详情'. Querying task status...",color='y')
                try:
                    # 使用正则表达式提取任务ID
                    import re
                    task_id_match = re.search(r'\d+', response.split("任务详情: ")[1])
                    if not task_id_match:
                        colored_print("无法从响应中提取任务ID",color='y')
                        return
                        
                    task_id = int(task_id_match.group())
                    task = self.task_manager.get_task(task_id)
                    if task:
                        fields_mapping = {
                            'id': 'ID',
                            'title': '标题',
                            'description': '描述',
                            'status': '状态',
                            'due_date': '截止日期',
                            'priority': '优先级',
                            'project_id': '项目ID',
                            'created_at': '创建时间',
                            'updated_at': '更新时间'
                        }
                        for key, value in task.items():
                            if key in fields_mapping:
                                print(f"{fields_mapping[key]}: {value}")
                            else:
                                print(f"任务 {task_id} 未知字段 {key}: {value}")
                        # 可以在这里添加更多任务信息输出
                except (ValueError, IndexError) as e:
                    colored_print(f"查询任务状态时出错: {str(e)}",color='r',log_level='error')
 
            # 设置提醒
            elif "已设置提醒:" in response:
                colored_print("AI responded with '已设置提醒'. Setting reminder...",color='y')
                try:
                    # 使用正则表达式提取任务ID
                    import re
                    task_id_match = re.search(r'\d+', response.split("已设置提醒: ")[1])
                    if not task_id_match:
                        print("\033[91m无法从响应中提取任务ID\033[0m")
                        return
                        
                    task_id = int(task_id_match.group())
                    
                    # 提取提醒时间
                    remind_time_match = re.search(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', response)
                    if not remind_time_match:
                        print("\033[91m无法从响应中提取提醒时间\033[0m")
                        return
                        
                    remind_time = remind_time_match.group()
                    
                    # 验证时间格式
                    datetime.datetime.strptime(remind_time, '%Y-%m-%d %H:%M:%S')
                    self.task_manager.add_reminder(task_id, remind_time)
                    colored_print(f"任务 {task_id} 设置提醒: {remind_time}",color='y')
                except ValueError as e:
                    colored_print(f"设置提醒时出错: {str(e)}",color='r',log_level='error')
            
            # 日程建议
            elif "日程建议:" in response:
                colored_print("AI responded with '日程建议'. Processing suggestions...",color='y')
                response.split("日程建议: ")[1].strip()
                # 这里可以添加更复杂的建议处理逻辑
                # print(f"建议内容: {suggestion}")
        
        except (ValueError, IndexError, sqlite3.Error) as e:
            colored_print(f"命令执行错误: {str(e)}",color='r',log_level='error')
            # 记录错误日志或通知用户

# 提醒服务
class ReminderService:
    def __init__(self, task_manager: TaskManager, nlp_processor: NLPProcessor):
        self.task_manager = task_manager
        self.nlp_processor = nlp_processor
        self.running = False
        self.thread = None
    
    def start(self):
        self.running = True
        self.thread = threading.Thread(target=self._run)
        self.thread.daemon = True
        self.thread.start()
    
    def stop(self):
        self.running = False
        if self.thread:
            self.thread.join()
    
    def _run(self):
        while self.running:
            reminders = self.task_manager.get_due_reminders()
            for reminder in reminders:
                message = f"提醒: {reminder['title']}"
                if reminder['description']:
                    message += f"\n详情: {reminder['description']}"
                
                colored_print(f"\n[提醒] {message}\n",color='m')
                self.task_manager.mark_reminder_notified(reminder['id'])
            
            time.sleep(60)  # 每分钟检查一次

# 主应用程序
class ScheduleAssistantApp:
    def __init__(self, config_file: str = 'assistant_config.ini'):

        # 初始化数据库
        init_db()
        logger.info("系统初始化开始")
        # 加载配置
        self.assistant_config = load_assistant_config(config_file)
        
        # 初始化组件
        self.task_manager = TaskManager()
        self.deepseek_client = DeepSeekClient(self.assistant_config)
        self.nlp_processor = NLPProcessor(self.task_manager, self.deepseek_client)
        self.reminder_service = ReminderService(self.task_manager, self.nlp_processor)
    
    def run(self):
        # 启动提醒服务
        self.reminder_service.start()
        
        # 显示欢迎信息和待办事项
        self._show_welcome()
        response=self.nlp_processor.process_command("列出当前任务")
        print(f"\033[93m\n{self.assistant_config.name}: \033[0m{response}")

        # 主循环
        try:
            while True:
                try:
                    print("\n\033[92m你说 (输入 'EOF' 结束): \033[0m")
                    lines = []
                    while True:
                        line = input()
                        if line == 'EOF':
                            break
                        lines.append(line)
                    user_input = '\n'.join(lines).strip()
                    if user_input.lower() in ['exit', 'quit', '退出']:
                        colored_print(f"正在退出系统...... ",'m')
                        break
                      
                    print(f"\033[91m正在思考...... \033[0m")
                    response = self.nlp_processor.process_command(user_input)
                    print(f"\033[93m\n{self.assistant_config.name}: \033[0m{response}")
                except Exception as e:
                    colored_print(f"\n{self.assistant_config.name}: 处理请求时出错：{str(e)}",color='r')
        finally:
            self.reminder_service.stop()
    
    def _show_welcome(self):
        welcome_msg = f"""
        ====================================
        欢迎使用日程管理系统!
        你的助手 {self.assistant_config.name} 为你服务。
        {self.assistant_config.name} 是一个{self.assistant_config.gender}性助手，
        性格: {self.assistant_config.personality}
        你可以用自然语言管理你的日程。
        你可以直接问我使用方法，我会告诉你隐藏的快捷命令。
        任务的状态只能：待处理、已完成、进行中
        输入 'exit' 或 '退出' 结人束话。
        ====================================
        """
        print(welcome_msg)

# 配置文件示例 (assistant_config.ini)
"""
[ASSISTANT]
Name = 小深
Gender = 女
Personality = 温柔体贴，善解人意，总是用鼓励的语气说话
Appearance = 穿着整洁的职业装，戴着圆框眼镜，总是带着温暖的微笑

[API]
Key = your_deepseek_api_key_here
URL = https://api.deepseek.com/v1/chat/completions
"""

if __name__ == "__main__":
    check_dependencies()
    app = ScheduleAssistantApp()
    app.run()